package com.cfh.springcloudclientapplication.customclient;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.stream.Stream;


public class RestClientsRegistrar implements ImportBeanDefinitionRegistrar,
        BeanFactoryAware, EnvironmentAware {

    private BeanFactory beanFactory;
    private Environment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
                                        BeanDefinitionRegistry registry) {
        Map<String, Object> attributes =
                metadata.getAnnotationAttributes(EnableRestClients.class.getName());

        ClassLoader classLoader = metadata.getClass().getClassLoader();

        Class<?>[] clientsClass = (Class<?>[]) attributes.get("clients");
        //接口类对象数组
        //筛选所有接口
        Stream.of(clientsClass)
                .filter(Class::isInterface) //仅选择接口
                .filter(interfaceClass ->
                        AnnotationUtils.findAnnotation(interfaceClass, RestClient.class) != null) //仅选择标注 @RestClient
                .forEach(restClientClass -> {
                    //获取 @RestClient 元信息
                    RestClient restClient = AnnotationUtils.findAnnotation(restClientClass,
                            RestClient.class);
                    //获取应用名称
                    String serviceName = environment.resolvePlaceholders(restClient.name());

                    Object proxyInstance = Proxy.newProxyInstance(classLoader, new Class[]{restClientClass},
                            new RequestMappingMethodInvocationHandler(serviceName, beanFactory));

                    String beanName = "RestClient." + serviceName;

                    if (registry instanceof SingletonBeanRegistry) {
                        SingletonBeanRegistry singletonBeanRegistry = (SingletonBeanRegistry) registry;
                        singletonBeanRegistry.registerSingleton(beanName, proxyInstance);
                    }
//                    registerBeanByFactoryBean(beanName,proxyInstance,restClientClass,registry);
                });
    }

    private static class RestClientClassFactorBean implements FactoryBean {
        private final Object proxyInstance;

        private final Class<?> restClientClass;

        private RestClientClassFactorBean(Object proxyInstance, Class<?> restClientClass) {
            this.proxyInstance = proxyInstance;
            this.restClientClass = restClientClass;
        }

        @Override
        public Object getObject() throws Exception {
            return proxyInstance;
        }

        @Override
        public Class<?> getObjectType() {
            return restClientClass;
        }
    }


    /**
     * 注册
     *
     * @param beanName
     * @param proxyInstance
     * @param restClientClass
     * @param registry
     */
    private void registerBeanByFactoryBean(String beanName, Object proxyInstance,
                                           Object restClientClass, BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder beanDefinitionBuilder =
                BeanDefinitionBuilder.genericBeanDefinition(RestClientClassFactorBean.class);

        beanDefinitionBuilder.addConstructorArgValue(proxyInstance);

        beanDefinitionBuilder.addConstructorArgValue(restClientClass);

        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

        registry.registerBeanDefinition(beanName, beanDefinition);
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
